package com.jsb.laundry.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.regex.Pattern;

import static java.util.Objects.isNull;

/**
 * 工具类
 *
 * @author zmy
 * @create 2022-11-18 0:20
 */
public class JSBUtil {

    private static final long serialVersionUID = -1185015143654744140L;

    private static final Logger LOGGER = LoggerFactory.getLogger(JSBUtil.class);

    /**
     * 生成ID的长度
     **/
    private static final Integer CREATE_ID_LENGTH = 11;
    /**
     * 生成合作会员卡ID的长度
     */
    private static final Integer CREATE_SVIP_ID_LENGTH = 5;
    /**
     * 价格转化常量
     **/
    public static final Integer UNIT_TURN = 100;

    /**
     * 复制证书时标识
     **/
    private static final String SPECIAL_FLAG = "JSB-20221107";

    /**
     * 输入流
     *
     * @param is
     * @return
     */
    public static String read(InputStream is) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            byte[] buffer = new byte[512];
            while ((len = is.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            return new String(bos.toByteArray(), 0, bos.size(), "utf-8");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";
    }
    /**
     * 生成订单编号
     * 格式为 20221118001658044000001
     *
     * @return
     */
    public static String generateLongOrderNO() {
        String orderNo = dateFormat.format(System.currentTimeMillis());
        long uniqueId = COUNTER.incrementAndGet() % 1000000;
        return String.format("%s%06d", orderNo, uniqueId);
    }
    /**
     * 原子计数器
     */
    private static AtomicLong COUNTER = new AtomicLong(0);
    private static final LongAdder IDADDER = new LongAdder();
    /**
     * 编号格式化
     */
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
    private static String lastTimestamp = "";
    /**
     * 用于：订单ID
     * 扩展生成编号，毫秒后6位，并发量高时教优秀
     *
     * @return
     */
    public static String generateLongOrderId() {
        String currentTimestamp = dateFormat.format(System.currentTimeMillis());
        // 如果时间戳更改，重置计数器
        if (!currentTimestamp.equals(lastTimestamp)) {
            lastTimestamp = currentTimestamp;
            IDADDER.reset();
        }
        IDADDER.increment();
        String number = String.format("%s%06d", currentTimestamp, IDADDER.longValue());
        LOGGER.info("生成订单编号==>" + number);
        return number;
    }
    /**
     * 商品价格 出库时调用
     *
     * @param databaseIntGoodsPrice 数据库中商品价格（分为单位 例如：999分）
     * @return
     */
    public static String turnDoubleStr(String databaseIntGoodsPrice) {
        Double output = Double.parseDouble(databaseIntGoodsPrice);
        Double price = output / UNIT_TURN;
        return String.valueOf(price);
    }
    /**
     * 商品价格 入库时调用
     *
     * @param inputPrice 商家定的商品价格（元为单位 例如：9.99元）或计算打折后的总价入库(9.99*0.75)
     * @return
     */
    public static String turnIntegerStr(String inputPrice) {
        Double price = Double.parseDouble(inputPrice);
        BigDecimal bigDecimal = new BigDecimal(inputPrice).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        BigDecimal multiply = bigDecimal.multiply(BigDecimal.valueOf(UNIT_TURN));
        int money = multiply.intValue();
        return String.valueOf(money);
    }


    /**
     * 图片出数据库时调用（图片地址分割符为逗号）
     * "gyueybuy,gdfufhnb,gfedubaf"转 List
     *
     * @param databasePic
     * @return
     */
    public static List strToListDatabaseOut(String databasePic){
        return Arrays.asList(databasePic.split(","));
    }

    /**
     * 图片存入数据库时调用 (外部list数据转string,分割符为逗号)
     *
     *
     * @param list
     * @return
     */
    public static String listToStrDatabaseInput(List list){
        return String.join(",", list);
    }

    /**
     * 复制证书
     * 源文件：wxpay/fileName
     * 生成目标：与jar包同级的 jsbConfig/wxpay/fileName
     *
     * @param fileName 要读取的文件名
     * @return
     */
    public static String getJarInnerResource(String fileName) {
        // 源文件目录
        String srcDir = "wxpay";
        // 要生成的目录
        String destDir = "jsbConfig/" + srcDir;
        //读取jar包内文件
        InputStream is = JSBUtil.class.getResourceAsStream("/" + srcDir + "/" + fileName);

        //若文件已存在，则返回的filePath中含有"JSB"，则不需再重写文件
        String filePath = createFile(destDir, fileName);

        //文件不存在时，则将流写入文件
        if (filePath.indexOf(SPECIAL_FLAG) == 0) {
            String path = filePath.substring(SPECIAL_FLAG.length());
            File file = new File(path);
            outputStream(is, file);
            return path;
        }
        return filePath;
    }

    /**
     * 创建目录、文件
     *
     * @param createDir
     * @param filename
     * @return
     */
    private static String createFile(String createDir, String filename) {
        String path = System.getProperty("user.dir");
        //create 文件夹
        String dirPath = path + "/" + createDir;
        File dir = new File(dirPath);
        dir.mkdirs();
        //create 文件
        String filePath = dirPath + "/" + filename;
        File file = new File(filePath);
        // 文件不存在时，则创建文件。返回的路径中有JSB20221107
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return SPECIAL_FLAG + filePath;
        }
        // 文件存在直接返回路径
        return filePath;
    }

    /**
     * 流写入到文件
     *
     * @param is
     * @param file
     */
    private static void outputStream(InputStream is, File file) {
        OutputStream os = null;
        try {
            os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            is.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * idea中
     * 相对路径读取项目中证书内容("/wxpay/apiclient_key.pem")
     *
     * @return
     */
    public static String getFileContentOfIDEA(String relativePath) {
        java.net.URL fileURL = JSBUtil.class.getClass().getResource(relativePath);
        if (fileURL == null) {
            return "没有读取到证书！";
        }
        File file = new File(fileURL.getPath());
        long fileLength = file.length();
        byte[] fileContent = new byte[(int) fileLength];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(fileContent);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(fileContent);
    }

    /**
     * idea中
     * 用相对路径获取证书的绝对路径("/wxpay/apiclient_key.pem")
     *
     * @return
     */
    public static String getFileAbsPathOfIDEA(String relativePath) {
        java.net.URL fileURL = JSBUtil.class.getClass().getResource(relativePath);
        if (fileURL == null) {
            return "没有读取到证书！";
        }
        String path = fileURL.getPath();
        String returnPath = path.substring(path.indexOf("/") + 1);
        return returnPath;
    }

    /**
     * 生成UUID
     *
     * @return
     */
    public static String generateUUID() {
        LOGGER.info("生成UUID");
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 生成指定长度字符串
     *
     * @param place
     * @return
     */
    public static String getRandomStr(int place) {
        String base = "myzwqetruioplkjhgfdsaxcvbnABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuffer sb = new StringBuffer();
        Random rd = new Random();
        for (int i = 0; i < place; i++) {
            sb.append(base.charAt(rd.nextInt(base.length())));
        }
        LOGGER.info("生成长度 => " + place + " 字符串 => " + sb);
        return sb.toString();
    }
    /**
     * 生成指定长度字符串
     *
     * @param place
     * @return
     */
    public static String getRandomNumberStr(int place) {
        String base = "0123456789";
        StringBuffer sb = new StringBuffer();
        Random rd = new Random();
        for (int i = 0; i < place; i++) {
            sb.append(base.charAt(rd.nextInt(base.length())));
        }
        LOGGER.info("生成长度 => " + place + " 字符串 => " + sb);
        return sb.toString();
    }

    /**
     * TODO 用于对添加时传对象的判断
     * 对象中有一个属性为空时，就返回true
     * 未对空字符串做判断
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static boolean hasNullFiledInObject(Object obj) {
        for (Field field : obj.getClass().getDeclaredFields()) {
            // 去掉serialVersionUID字段的判断
            if (field.getName().equals("serialVersionUID"))
                continue;
            field.setAccessible(true);
            try {
                if (field.get(obj) == null) {
                    return true;
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        //TODO 所有属性都不为空时返回false
        return false;
    }

    /**
     * 时间格式化
     *
     * @param date
     * @return
     */
    public static String dateFormat(Date date, String format) {
        SimpleDateFormat sf = new SimpleDateFormat(format);
        return sf.format(date);
    }

    /**
     * 默认时间格式化为 yyyy-MM-dd HH:mm:ss
     *
     * @param date
     * @return
     */
    public static String dateFormat(Date date) {
        return dateFormat(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 时间比较
     * 1早于2 返回true
     * 其余返回 false
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean dateCompare(Date date1, Date date2) {
        if (date1.before(date2)) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 对象判空
     */
    public static boolean isEmptyObject(Object object) {
        return object == null;
    }

    /**
     * 判断一个对象是否是数组[]类型（Java基本型别的数组）
     *
     * @param object 对象
     * @return true：是数组  false：不是数组
     */
    public static boolean isArray(Object object) {
        return !isEmptyObject(object) && object.getClass().isArray();
    }

    /**
     * 字符串判空
     */
    public static boolean isEmptyString(String str) {
        return isNull(str) || "".equals(str.trim()) || "null".equalsIgnoreCase(str) || "undefined".equalsIgnoreCase(str);
    }

    /**
     * 集合判空(map、list、set)
     */
    public static boolean isEmptyCollection(Collection<?> coll) {
        return isNull(coll) || coll.isEmpty();
    }

    public static boolean isEmptyMap(Map<?, ?> map) {
        return isNull(map) || map.isEmpty();
    }

    /**
     * 去字符串前后空格
     */
    public static String trim(String str) {
        return (str == null ? "" : str.trim());
    }

    /**
     * 驼峰转下划线命名
     * 例如：userName -> user_name
     */
    public static String humpToUnderlined(String str) {
        if (str == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean currentCharIsUpperCase = true;
        // 下一个字符是否大写
        boolean nextCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (i > 0) {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            } else {
                preCharIsUpperCase = false;
            }

            currentCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1)) {
                nextCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if (preCharIsUpperCase && currentCharIsUpperCase && !nextCharIsUpperCase) {
                sb.append("_");
            } else if ((i != 0 && !preCharIsUpperCase) && currentCharIsUpperCase) {
                sb.append("_");
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }

    /**
     * 驼峰式命名法
     * 例如：user_name -> userName
     */
    public static String underlinedToHump(String s) {
        if (s == null) {
            return null;
        }
        if (s.indexOf("_") == -1) {
            return s;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if ("_".equals(c)) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 实体类转Map
     *
     * @param object
     * @return
     */
    public static Map<String, Object> entityToMap(Object object) {
        Map<String, Object> map = new HashMap<>();
        for (Field field : object.getClass().getDeclaredFields()) {
            try {
                boolean flag = field.isAccessible();
                field.setAccessible(true);
                Object o = field.get(object);
                map.put(field.getName(), o);
                field.setAccessible(flag);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * Map转实体类
     *
     * @param map    需要初始化的数据，key字段必须与实体类的成员名字一样，否则赋值为空
     * @param entity 需要转化成的实体类
     */
    public static <T> T mapToEntity(Map<String, Object> map, Class<T> entity) {
        T t = null;
        try {
            t = entity.newInstance();
            for (Field field : entity.getDeclaredFields()) {
                if (map.containsKey(field.getName())) {
                    boolean flag = field.isAccessible();
                    field.setAccessible(true);
                    Object object = map.get(field.getName());
                    if (object != null && field.getType().isAssignableFrom(object.getClass())) {
                        field.set(t, object);
                    }
                    field.setAccessible(flag);
                }
            }
            return t;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 生成11位长度ID
     * 00000000019
     *
     * @param num
     * @return
     */
    public static String generateID(long num) {
        LOGGER.info("生成ID");
        // 指定生成字符串的长度11位
        return String.format("%0" + CREATE_ID_LENGTH + "d", num);
    }

    /**
     * 生成合作会员卡编号(5位)
     * 内部预留20000张
     * 20001
     *
     * @param num
     * @return
     */
    public static String generateSVIPCardID(int num) {
        LOGGER.info("生成合作会员卡编号");
        // 指定生成字符串的长度5位
        return String.format("%0" + CREATE_SVIP_ID_LENGTH + "d", num + 20000);
    }

    /**
     * 时间增加
     *
     * @param sourceDate
     * @param year
     * @return
     */
    public static Date stepMonth(Date sourceDate, int year) {
        Calendar c = Calendar.getInstance();
        c.setTime(sourceDate);
        c.add(Calendar.YEAR, year);
        return c.getTime();
    }

    /**
     * 校验手机号
     * @param phoneNumber
     * @return
     */
    public static boolean isValidPhoneNumber(String phoneNumber) {
        if (!JSBUtil.isEmptyString(phoneNumber)) {
            return Pattern.matches("^1[3-9]\\d{9}$", phoneNumber);
        }
        return false;
    }

    private static final int MAX_LENGTH = 32;

    /**
     * 随机数生成器生成32个字节的随机字节数组，并使用 Base64 编码将其转换为字符串。
     * 由于 Base64 编码后的字符串长度为 44，因此我们将选择前面的32个字符作为结果。
     *
     * @return
     */
    public static String generateString() {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[MAX_LENGTH];
        random.nextBytes(bytes);
        return Base64.getUrlEncoder()
                .withoutPadding()
                .encodeToString(bytes)
                .substring(0, MAX_LENGTH);
    }

    /**
     * 手机号掩码
     * @param phoneNumber
     * @return
     */
    public static String maskPhoneNumber(String phoneNumber) {
        StringBuilder maskedNumber = new StringBuilder();
        int length = phoneNumber.length();

        // 前三位保留原始显示
        maskedNumber.append(phoneNumber, 0, 3);

        // 使用 * 掩盖中间四位数字
        for (int i = 3; i < length - 4; i++) {
            maskedNumber.append("*");
        }

        // 后四位保留原始显示
        maskedNumber.append(phoneNumber, length - 4, length);

        return maskedNumber.toString();
    }

    /**
     * 获取用户ip
     *
     * @param request
     * @return
     */
    public static String getRemoteHost(HttpServletRequest request) {
        if (request != null) {
            try {
                String ip = request.getHeader("X-Real-IP");
                if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("x-forwarded-for");
                }
                if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("Proxy-Client-IP");
                }
                if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("WL-Proxy-Client-IP");
                }
                if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getRemoteAddr();
                }
                return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
            } catch (Exception e) {
                e.printStackTrace();
                return "127.0.0.1";
            }
        } else {
            return "127.0.0.1";
        }
    }
}
